package com.derbysoft.dswitch.adapter.ihg.dswitch3;

import com.derbysoft.dswitch.adapter.ihg.common.domain.OperationType;
import com.derbysoft.dswitch.adapter.ihg.common.domain.ReservationLog;
import com.derbysoft.dswitch.adapter.ihg.common.domain.TransactionLog;
import com.derbysoft.dswitch.adapter.ihg.common.log.*;
import com.derbysoft.dswitch.adapter.ihg.common.repository.ReservationLogRepository;
import com.derbysoft.dswitch.adapter.ihg.common.repository.TransactionLogRepository;
import com.derbysoft.dswitch.adapter.ihg.dswitch3.service.handler.RemoteServiceHandler;
import com.derbysoft.dswitch.adapter.ihg.dswitch3.service.impl.BaseHotelRemoteService;
import com.derbysoft.dswitch.dto.hotel.common.TransactionActionType;
import com.derbysoft.dswitch.dto.hotel.reservation.ReservationRoomStayDTO;
import com.derbysoft.dswitch.remote.hotel.dto.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;

/**
 * Created by: jason
 * Date: 2012-05-15
 */
@Service("hotelProviderWebService")
public class HotelRemoteServiceProxy extends BaseHotelRemoteService {

    @Value("${dswitch3.adapter}")
    protected String adapter;

    @Autowired
    @Qualifier("hotelAvailabilityHandler")
    private RemoteServiceHandler<HotelAvailRequest, HotelAvailResponse> hotelAvailHandler;

    @Autowired
    @Qualifier("hotelMultiAvailabilityHandler")
    private RemoteServiceHandler<HotelAvailRequest, HotelAvailResponse> hotelMultiAvailHandler;

    @Autowired
    @Qualifier("hotelPreviewHandler")
    private RemoteServiceHandler<HotelReservationRequest, HotelReservationResponse> hotelPreviewHandler;

    @Autowired
    @Qualifier("hotelReservationHandler")
    private RemoteServiceHandler<HotelReservationRequest, HotelReservationResponse> hotelReservationHandler;

    @Autowired
    @Qualifier("hotelModifyReservationHandler")
    private RemoteServiceHandler<HotelReservationRequest, HotelReservationResponse> hotelModifyReservationHandler;

    @Autowired
    @Qualifier("hotelCancelHandler")
    private RemoteServiceHandler<HotelCancelRequest, HotelCancelResponse> hotelCancelHandler;

    @Autowired
    private TransactionLogRepository transactionLogRepository;

    @Autowired
    private ReservationLogRepository reservationLogRepository;

    @Autowired
    @Qualifier("taskExecutor")
    private Executor executor;

    @Override
    public HotelAvailResponse getNoCachedAvailability(HotelAvailRequest request) {
        PerformanceLogger.init();

        List<String> hotelCodes = request.getHotelAvailRQ().getAvailCriteria().getHotelCodesList();
        if (hotelCodes.size() > 1) {
            return getMultiAvailability(request);
        }
        return getSingleAvailability(request);
    }

    private HotelAvailResponse getSingleAvailability(HotelAvailRequest request) {
        PerformanceLogger.append(LogBody.REQUEST_TYPE, OperationType.HOTEL_AVAIL.name());
        setAvailLog(request);

        HotelAvailResponse hotelAvailResponse = hotelAvailHandler.invoke(request);

        PerformanceLogger.save(executor);
        saveLogs();
        return hotelAvailResponse;
    }

    private HotelAvailResponse getMultiAvailability(HotelAvailRequest request) {
        PerformanceLogger.append(LogBody.REQUEST_TYPE, OperationType.HOTEL_MULTI_AVAIL.name());
        setAvailLog(request);

        HotelAvailResponse hotelMultiAvailResponse = hotelMultiAvailHandler.invoke(request);

        PerformanceLogger.save(executor);
        saveLogs();
        return hotelMultiAvailResponse;
    }

    @Override
    public HotelReservationResponse makeReservation(HotelReservationRequest request) {
        PerformanceLogger.init();
        HotelReservationResponse reservationResponse;
        setReservationLog(request);
        if (request.getHotelReservationRQ().getResActionType() == TransactionActionType.INITIATE) {
            PerformanceLogger.append(LogBody.REQUEST_TYPE, OperationType.HOTEL_PREVIEW.name());
            reservationResponse = hotelPreviewHandler.invoke(request);
        } else if (request.getHotelReservationRQ().getResActionType() == TransactionActionType.BOOK) {
            PerformanceLogger.append(LogBody.REQUEST_TYPE, OperationType.HOTEL_RESERVATION.name());
            reservationResponse = hotelReservationHandler.invoke(request);
        } else {
            PerformanceLogger.append(LogBody.REQUEST_TYPE, OperationType.HOTEL_MODIFY_RESERVATION.name());
            reservationResponse = hotelModifyReservationHandler.invoke(request);
        }
        PerformanceLogger.save(executor);
        saveLogs();
        return reservationResponse;
    }

    @Override
    public HotelCancelResponse cancelReservation(HotelCancelRequest request) {
        PerformanceLogger.init();
        PerformanceLogger.append(LogBody.REQUEST_TYPE, OperationType.CANCEL_RESERVATION.name());
        setPerformanceLogHeader(request);
        HotelCancelResponse cancelResponse = hotelCancelHandler.invoke(request);
        PerformanceLogger.save(executor);
        saveLogs();
        return cancelResponse;
    }

    private void setReservationLog(HotelReservationRequest request) {
        ReservationRoomStayDTO roomStayDTO = request.getHotelReservationRQ().getHotelReservation().getRoomStaysList().get(0);
        setPerformanceLogHeader(request);
        PerformanceLogger.append(LogBody.DERBY_HOTEL_CODES, request.getHotelReservationRQ().getHotelReservation().getHotelCode());
        PerformanceLogger.append(LogBody.CHECK_IN, roomStayDTO.getStayDateRange().getCheckin());
        PerformanceLogger.append(LogBody.CHECK_OUT, roomStayDTO.getStayDateRange().getCheckout());
        PerformanceLogger.append(LogBody.RATE_PLAN, roomStayDTO.getRatePlanCode());
        PerformanceLogger.append(LogBody.ROOM_TYPE, roomStayDTO.getRoomTypeCode());
        PerformanceLogger.append(LogBody.NUMBER_OF_UNIT, String.valueOf(roomStayDTO.getNumberOfUnits()));
        PerformanceLogger.append(LogBody.ADULT, String.valueOf(roomStayDTO.getAdultCount()));
        PerformanceLogger.append(LogBody.CHILD, String.valueOf(roomStayDTO.getChildCount()));
        PerformanceLogger.append(LogBody.ERSP_NUMBER, request.getHotelReservationRQ().getErsp());
    }

    private void setAvailLog(HotelAvailRequest request) {
        setPerformanceLogHeader(request);
        PerformanceLogger.append(LogBody.DERBY_HOTEL_CODES, getHotelCodes(request.getHotelAvailRQ()));
        PerformanceLogger.append(LogBody.CHECK_IN, request.getHotelAvailRQ().getAvailCriteria().getStayDateRange().getCheckin());
        PerformanceLogger.append(LogBody.CHECK_OUT, request.getHotelAvailRQ().getAvailCriteria().getStayDateRange().getCheckout());
        PerformanceLogger.append(LogBody.NUMBER_OF_UNIT, String.valueOf(request.getHotelAvailRQ().getAvailCriteria().getRoomStayCandidate().getNumberOfUnits()));
        PerformanceLogger.append(LogBody.ADULT, String.valueOf(request.getHotelAvailRQ().getAvailCriteria().getRoomStayCandidate().getAdultCount()));
        PerformanceLogger.append(LogBody.CHILD, String.valueOf(request.getHotelAvailRQ().getAvailCriteria().getRoomStayCandidate().getChildCount()));
    }

    private String getHotelCodes(com.derbysoft.dswitch.dto.hotel.avail.HotelAvailRQ hotelAvailRQ) {
        StringBuilder builder = new StringBuilder();
        for (String s : hotelAvailRQ.getAvailCriteria().getHotelCodesList()) {
            builder.append(s).append(" ");
        }
        return builder.toString();
    }

    private void setPerformanceLogHeader(AbstractRQ request) {
        PerformanceLogger.append(LogBody.TASK_ID, request.getHeader().getTaskId());
        PerformanceLogger.append(LogBody.CHANNEL, request.getHeader().getSource());
        PerformanceLogger.append(LogBody.PROVIDER_CHAIN, "ihg");
    }

    private void saveLogs() {
        TransactionLog transactionLog = TransactionLogger.getAvailLogger().get();
        Set<LogDetailPair> logDetailPairs = TransactionLogger.getLogDetails().get();
        if (transactionLog != null) {
            executor.execute(new TransactionLogTask(transactionLogRepository, transactionLog, logDetailPairs));
            TransactionLogger.cleanAvailLogger();
        }
        ReservationLog reservationLog = TransactionLogger.getReservationLogger().get();
        if (reservationLog != null) {
            executor.execute(new ReservationLogTask(reservationLogRepository, reservationLog, logDetailPairs));
            TransactionLogger.cleanReservationLogger();
        }
        TransactionLogger.cleanLogDetails();
    }
}
